\bibliographystyle{gbt7714-2005}

\chapter{命名空间}
\epigraphhead[60]{\epigraph{消除无关事物之间的影响。}{\textit{Andrew Hunt《The Pragmatic Programmer》}}}

什么是命名空间？从广义上来说，命名空间是一种封装事物的方法。在很多地方都可以见到这种抽象概念。例如，在操作系统中目录用来将相关文件分组，对于目录中的文件来说，它就扮演了命名空间的角色。具体举个例子，文件 foo.txt 可以同时在目录/home/greg 和 /home/other 中存在，但在同一个目录中不能存在两个 foo.txt 文件。另外，在目录 /home/greg 外访问 foo.txt 文件时，我们必须将目录名以及目录分隔符放在文件名之前得到 /home/greg/foo.txt。这个原理应用到程序设计领域就是命名空间的概念。

在PHP中，命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题：\cite{PHPGroup，2016b}
\begin{enumerate}
	\item 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
	\item 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名（或简短）的名称，提高源代码的可读性。
\end{enumerate}

\section{定义命名空间}
命名空间通过关键字namespace 来声明。如果一个文件中包含命名空间，它必须在其它所有代码之前声明命名空间，任意合法的PHP代码都可以包含在命名空间中，但只有以下类型的代码受命名空间的影响，它们是：类（包括抽象类和traits）、接口、函数和常量。

\begin{lstlisting}
<?php
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

?>
\end{lstlisting}

与目录和文件的关系很象，PHP 命名空间也允许指定层次化的命名空间的名称。因此，命名空间的名字可以使用分层次的方式定义：

\begin{lstlisting}
<?php
namespace MyProject\Sub\Level;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

?>
\end{lstlisting}

上面的例子创建了常量MyProjectSub\textbackslash{}Level\textbackslash{}CONNECT\_OK，类 MyProject\textbackslash{}Sub\textbackslash{}Level\textbackslash{}Connection和函数 MyProject\textbackslash{}Sub\textbackslash{}Level\textbackslash{}connect。

\section{使用命名空间}

类似于文件系统，PHP命名空间中的元素可以通过三种方式引用：
\begin{enumerate}
	\item 非限定名称，或不包含前缀的名称，意味着使用当前命名空间中的元素。
	\item 限定名称,或包含前缀的名称，意味着使用子空间中的元素。
	\item 完全限定名称，或包含了全局前缀操作符的名称，就使用指定空间的元素。
\end{enumerate}

下面是这三种方式的例子，先定义file1.php：

\begin{lstlisting}
<?php
namespace Foo\Bar\subnamespace;

const FOO = 1;
function foo() {}
class foo
{
    static function staticmethod() {}
}
?>
\end{lstlisting}

file2.php

\begin{lstlisting}
<?php
namespace Foo\Bar;
include 'file1.php';

const FOO = 2;
function foo() {}
class foo
{
    static function staticmethod() {}
}

/* 非限定名称 */
foo(); // 解析为 Foo\Bar\foo resolves to function Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo的静态方法staticmethod。resolves to class Foo\Bar\foo, method staticmethod
echo FOO; // resolves to constant Foo\Bar\FOO

/* 限定名称 */
subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // 解析为类 Foo\Bar\subnamespace\foo,
                                  // 以及类的方法 staticmethod
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO

/* 完全限定名称 */
\Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO
?>
\end{lstlisting}

如果没有定义任何命名空间，所有的类与函数的定义都是在全局空间，与 PHP 引入命名空间概念前一样。在名称前加上前缀 \textbackslash{} 表示该名称是全局空间中的名称，即使该名称位于其它的命名空间中时也是如此。注意访问任意全局类、函数或常量，都可以使用完全限定名称，例如 \textbackslash{}strlen()。

\section{别名/导入}
允许通过别名引用或导入外部的完全限定名称，是命名空间的一个重要特征。PHP 5.6开始允许导入函数或常量或者为它们设置别名。在PHP中，别名是通过操作符 use 来实现的:

\begin{lstlisting}
<?php
namespace foo;
use My\Full\Classname as Another;

// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;

// 导入一个全局类
use ArrayObject;

// importing a function (PHP 5.6+)
use function My\Full\functionName;

// aliasing a function (PHP 5.6+)
use function My\Full\functionName as func;

// importing a constant (PHP 5.6+)
use const My\Full\CONSTANT;

$obj = new namespace\Another; // 实例化 foo\Another 对象
$obj = new Another; // 实例化 My\Full\Classname　对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象
echo CONSTANT; // 显示常量 My\Full\CONSTANT
?>
\end{lstlisting}

\bibliography{../bib/yangjh}
